package fpzhan.plane.program.connect;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import fpzhan.plane.program.compose.ComposeContent;
import fpzhan.plane.program.describe.*;
import fpzhan.plane.program.function.ChainCodeBlockFunction;
import fpzhan.plane.program.function.CodeBlockFunction;
import fpzhan.plane.program.function.ConditionFunction;
import fpzhan.plane.program.compose.CodeBlockCompose;
import fpzhan.plane.program.param.RemoveParamTool;
import fpzhan.plane.program.util.ListUtils;
import fpzhan.plane.program.util.SetUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Consumer;

public class ChainCodeBlockConnect extends CodeBlockConnect implements Cloneable {

    private final List<CodeBlockCompose> chainBlocks = new ArrayList<>();

    private List<ComposeContent> contents = new ArrayList<>();

    protected void chainConnect(ChainCodeBlockConnect connect){
        chainBlocks.addAll(connect.chainBlocks);
        contents.addAll(connect.contents);
    }

    protected ChainCodeBlockConnect addChainBlocks(List<CodeBlockCompose> composes) {
        return this.addChainBlocks(composes, null);
    }

    protected ChainCodeBlockConnect addChainBlocks(List<CodeBlockCompose> composes, List<ComposeContent> composeContents) {
        chainBlocks.addAll(composes);
        this.getCurrent().getBeginFlow().values().forEach(list -> list.addAll(composes));
        if (ListUtils.isEmpty(composeContents)) {
            composes.forEach(
                    i -> contents.add(new ComposeContent(new ArrayList<>(this.getCurrent().getBeginFlow().keySet())))
            );
        } else {
            composeContents.forEach(content -> content.setBelongToSignName(new ArrayList<>(this.getCurrent().getBeginFlow().keySet())));
            contents.addAll(composeContents);
        }

        return this;
    }

    public ChainCodeBlockConnect(CodeBlockContext context, Object source) {
        super(context, source);
    }

    public CheckParamForLose<ChainCodeBlockCompose,ChainCodeBlockConnect> execute(CodeBlockFunction function) {
        ChainCodeBlockCompose compose = new ChainCodeBlockCompose(function);
        addChainBlocks(Collections.singletonList(compose));
        CheckParamForLose lose = new CheckParamForLose(compose, this);
        return lose;
    }

    public CodeBlockComment<ChainCodeBlockCompose,ChainCodeBlockConnect> execute(AddParamTool addParamTool){
        CheckParamForLose lose = execute(addParamTool.end());
        if(SetUtils.isNotEmpty(addParamTool.getAdds())){
            return lose.addc((String []) addParamTool.getAdds().toArray(new String[]{}));
        }
        return lose.none();
    }

    public CodeBlockComment<ChainCodeBlockCompose,ChainCodeBlockConnect> execute(RemoveParamTool removes){
        return execute(removes.remove()).losec(removes.getRemoves());
    }


    public ComposeComment<ConditionCodeBlockCompose,ConditionMixCodeBlockConnect> branch() {
        ConditionMixCodeBlockConnect conditionConnect = new ConditionMixCodeBlockConnect(getContext(), this);
        return new ComposeComment(( conditionConnect.aloneCodeBlockCompose), conditionConnect);
    }

    public ConditionMixCodeBlockConnect exitIfs() {
        return (ConditionMixCodeBlockConnect) getSource();
    }

    public ConditionMixCodeBlockConnect exitDefaults(){
        return exitIfs();
    }

    public AloneBranchComment aloneBranch(ConditionFunction condition) {
        return new AloneBranchComment((comment) -> this.branch().comment(comment).ifs(condition).comment(comment));
    }

    public ChainCodeBlockConnect aloneBranchEnd() {
        return exitIfs().branchEnd();
    }

    public ComposeComment<LoopCodeBlockCompose,ChainCodeBlockConnect> loop(LoopParam loopParam) {
        return new LoopMixCodeBlockConnect(getContext(), this)
                .loop(loopParam.getFunction(), loopParam.getLoopElementKey(), loopParam.getLoopIndexKey(), loopParam.getLoopSizeKey());
    }

    public ComposeComment<LoopCodeBlockCompose,ChainCodeBlockConnect> loop(String getKey, String loopElementKey, String loopIndexKey, String loopSizeKey) {
        return new LoopMixCodeBlockConnect(getContext(), this).loop(getKey, loopElementKey, loopIndexKey, loopSizeKey);
    }

    public ComposeComment<LoopCodeBlockCompose,ChainCodeBlockConnect> loop(String getKey, String loopElementKey, String loopIndexKey) {
        return loop(getKey, loopElementKey, loopIndexKey, null);
    }

    public ComposeComment<LoopCodeBlockCompose,ChainCodeBlockConnect> loop(String getKey, String loopElementKey) {
        return loop(getKey, loopElementKey, null, null);
    }



    public ChainCodeBlockConnect loopEnd() {
        return ((LoopMixCodeBlockConnect) getSource()).loopEnd();
    }

    public ComposeComment<ScopeCodeBlockCompose,ChainCodeBlockConnect> scope(){
        return new ScopeMixCodeBlockConnect(getContext(),this).scope();
    }

    public ChainCodeBlockConnect scopeEnd() {
        return ((ScopeMixCodeBlockConnect) getSource()).scopeEnd();
    }


    public CodeBlockComment<TryCatchCodeBlockCompose,ChainCodeBlockConnect> trys(){
        return new TryCatchMixCodeBlockConnect(getContext(),this).trys();
    }

    public <T extends Exception> CheckParamForLose<TryCatchCodeBlockCompose,TryCatchMixCodeBlockConnect> catchs(Class<T> eClass,CodeBlockFunction catchFunction){
        return ((TryCatchMixCodeBlockConnect)getSource()).catchs(eClass,catchFunction);
    }


    public  RunFlow flowEnd() {
        return (RunFlow) getSource();
    }

    protected CodeBlockFunction compose(JSONArray proxyArray, JSONArray structArray,
                                        ComposeContent content) throws Exception {
        ChainCodeBlockFunction function = param -> {};
        int i = 0;
        while (i < chainBlocks.size()) {
            CodeBlockCompose compose = chainBlocks.get(i);
            JSONObject jsonObject = new JSONObject(new LinkedHashMap<>());
            JSONObject structObject = new JSONObject(new LinkedHashMap<>());
            function = function.after(compose.compose(getProxys(), jsonObject, getStruct(), structObject, contents.get(i).extend(content)));
            proxyArray.add(jsonObject);
            if (structObject != null && structObject.size() > 0) structArray.add(structObject);
            i++;
        }
        return function;
    }

    public SignBeginComment signBegin(String name) {
        this.getCurrent().getBeginFlow().put(name, new ArrayList<>());
        return new SignBeginComment(this, name);
    }

    public ChainCodeBlockConnect signEnd(String name) {
        if (this.getCurrent().getBeginFlow().keySet().contains(name)) {
            this.getFinishFlow().put(name, this.getCurrent().getBeginFlow().get(name));
            this.getCurrent().getBeginFlow().remove(name);
        }
        return this;
    }


    public RepeatComment repeat(String name) {
        Consumer<String> supplier = comment -> {
            List<CodeBlockCompose> list = this.getFinishFlow().get(name);
            if (ListUtils.isNotEmpty(list)) {
                Integer repeatTime = this.getContext().getRepeatTime().get(name);
                if (repeatTime == null) {
                    this.getContext().getRepeatTime().put(name, 1);
                } else {
                    this.getContext().getRepeatTime().put(name, repeatTime + 1);
                }
                List<ComposeContent> composeContents = new ArrayList<>();
                for (int i = 0; i < list.size(); i++) {
                    composeContents.add(new ComposeContent(
                            i == 0,
                            i == (list.size() - 1),
                            name,
                            new Integer(this.getContext().getRepeatTime().get(name)),
                            this.getContext().getBeginFlowComment().get(name),
                            comment
                    ));
                }
                addChainBlocks(list, composeContents);
            }
        };
        return new RepeatComment(this, supplier);
    }

}
